package com.zws.cucumber.optimization.socket.oio;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

public class ZwsSocketOutputStream extends OutputStream {

    private static final Logger logger = LoggerFactory.getLogger(ZwsSocketOutputStream.class);

    private static final ThreadLocal<ByteBuffer> bufferThreadLocal = ThreadLocal.withInitial(() -> ByteBuffer.allocate(32 * 1024));

    private boolean first = true;
    private ByteBuffer outputBuffer; // 128k
    private ZwsSocketInputStream inputStream;

    public ZwsSocketOutputStream() {
    }

    @Override
    public void write(int b) throws IOException {
        if (logger.isTraceEnabled()) {
            logger.trace("zws, write(int): {}, {}, {}, {}", b, (char) b, outputBuffer, System.identityHashCode(outputBuffer));
        }
        handleIfFirstWrite();
        outputBuffer.put((byte) b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        if (logger.isTraceEnabled()) {
            logger.trace("write(byte[]): {}, {}, {}", outputBuffer, System.identityHashCode(outputBuffer), new String(b));
        }
        handleIfFirstWrite();
        outputBuffer.put(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        if (logger.isTraceEnabled()) {
            logger.trace("zws, write(byte[], int, int), {}, {}, {}}", outputBuffer, System.identityHashCode(outputBuffer), new String(b, off, len));
        }
        handleIfFirstWrite();
        outputBuffer.put(b, off, len);
    }

    private void handleIfFirstWrite() {
        if (first) {
            first = false;
            outputBuffer = bufferThreadLocal.get();
            outputBuffer.clear();
            inputStream.resetState();
        }
    }

    public ZwsSocketInputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(ZwsSocketInputStream inputStream) {
        this.inputStream = inputStream;
    }

    public void resetState() {
        first = true;
        outputBuffer.clear();
    }

    ByteBuffer getOutputBuffer() {
        return outputBuffer;
    }
}
